package com.audition.thread.day02.introduce;

/**
 * @author xukang
 * @date 2021-03-10 16:06
 */
public class Introduce {
/**
 * Lock 接口是 Java 5 引入的，最常见的实现类是 ReentrantLock，可以起到“锁”的作用。
 * Lock 和 synchronized 是两种最常见的锁，锁是一种工具，用于控制对共享资源的访问，
 * 而  Lock 和 synchronized 都可以达到线程安全的目的，但是在使用上和功能上又有较大的不同。
 * 所以 Lock 并不是用来代替 synchronized 的，而是当使用 synchronized 不合适或不足以满足要求的时候，
 * Lock 可以用来提供更高级功能的。
 * 通常情况下，Lock 只允许一个线程来访问这个共享资源。不过有的时候，一些特殊的实现也可允许并发访问，
 * 比如 ReadWriteLock 里面的 ReadLock。
 * <p>
 * public interface Lock {
 * void lock();
 * void lockInterruptibly() throws InterruptedException;
 * boolean tryLock();
 * boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
 * void unlock();
 * Condition newCondition();
 * }
 * <p>
 * 与此同时，lock() 方法不能被中断，这会带来很大的隐患：一旦陷入死锁，lock() 就会陷入永久等待，所以一般我们用 tryLock() 等其他更高级的方法来代替 lock()，下面我们就看一看 tryLock() 方法。
 * <p>
 * public interface Lock {
 * void lock();
 * void lockInterruptibly() throws InterruptedException;
 * boolean tryLock();
 * boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
 * void unlock();
 * Condition newCondition();
 * }
 * <p>
 * 与此同时，lock() 方法不能被中断，这会带来很大的隐患：一旦陷入死锁，lock() 就会陷入永久等待，
 * 所以一般我们用 tryLock() 等其他更高级的方法来代替 lock()，下面我们就看一看 tryLock() 方法。
 * <p>
 * lockInterruptibly()
 * 这个方法的作用就是去获取锁，如果这个锁当前是可以获得的，那么这个方法会立刻返回，但是如果这个锁当前是不能获得的（被其他线程持有），那么当前线程便会开始等待，除非它等到了这把锁或者是在等待的过程中被中断了，否则这个线程便会一直在这里执行这行代码。一句话总结就是，除非当前线程在获取锁期间被中断，否则便会一直尝试获取直到获取到为止。
 * 顾名思义，lockInterruptibly() 是可以响应中断的。相比于不能响应中断的 synchronized 锁，lockInterruptibly() 可以让程序更灵活，可以在获取锁的同时，保持对中断的响应。我们可以把这个方法理解为超时时间是无穷长的 tryLock(long time, TimeUnit unit)，因为 tryLock(long time, TimeUnit unit) 和 lockInterruptibly() 都能响应中断，只不过 lockInterruptibly() 永远不会超时。
 * 这个方法本身是会抛出 InterruptedException 的，所以使用的时候，如果不在方法签名声明抛出该异常，那么就要写两个 try 块，如下所示
 */
/**
 * public interface Lock {
 *     void lock();
 *     void lockInterruptibly() throws InterruptedException;
 *     boolean tryLock();
 *     boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
 *     void unlock();
 *     Condition newCondition();
 * }
 */
/**
 * 与此同时，lock() 方法不能被中断，这会带来很大的隐患：一旦陷入死锁，lock() 就会陷入永久等待，
 * 所以一般我们用 tryLock() 等其他更高级的方法来代替 lock()，下面我们就看一看 tryLock() 方法。
 *  public void tryLock(Lock lock1, Lock lock2) throws InterruptedException {
 *         while (true) {
 *             if (lock1.tryLock()) {
 *                 try {
 *                     if (lock2.tryLock()) {
 *                         try {
 *                             System.out.println("获取到了两把锁，完成业务逻辑");
 *                             return;
 *                         } finally {
 *                             lock2.unlock();
 *                         }
 *                     }
 *                 } finally {
 *                     lock1.unlock();
 *                 }
 *             } else {
 *                 Thread.sleep(new Random().nextInt(1000));
 *             }
 *         }
 *     }
 */
/**
 * lockInterruptibly()
 * 这个方法的作用就是去获取锁，如果这个锁当前是可以获得的，那么这个方法会立刻返回，但是如果这个锁当前是不能获得的（被其他线程持有），
 * 那么当前线程便会开始等待，除非它等到了这把锁或者是在等待的过程中被中断了，
 * 否则这个线程便会一直在这里执行这行代码。一句话总结就是，除非当前线程在获取锁期间被中断，
 * 否则便会一直尝试获取直到获取到为止。
 * 顾名思义，lockInterruptibly() 是可以响应中断的。相比于不能响应中断的 synchronized 锁，
 * lockInterruptibly() 可以让程序更灵活，可以在获取锁的同时，保持对中断的响应。
 * 我们可以把这个方法理解为超时时间是无穷长的 tryLock(long time, TimeUnit unit)，
 * 因为 tryLock(long time, TimeUnit unit) 和 lockInterruptibly() 都能响应中断，
 * 只不过 lockInterruptibly() 永远不会超时。
 * 这个方法本身是会抛出 InterruptedException 的，所以使用的时候，如果不在方法签名声明抛出该异常，
 * 那么就要写两个 try 块，如下所示
 * public void lockInterruptibly() {
 *         try {
 *             lock.lockInterruptibly();
 *             try {
 *                 System.out.println("操作资源");
 *             } finally {
 *                 lock.unlock();
 *             }
 *         } catch (InterruptedException e) {
 *             e.printStackTrace();
 *         }
 *     }
 */
/**
 * 源码分析
 * 下面我们来分析公平和非公平锁的源码，具体看下它们是怎样实现的，可以看到在 ReentrantLock 类包含一个 Sync 类，这个类继承自AQS（AbstractQueuedSynchronizer），代码如下：
 * 复制代码public class ReentrantLock implements Lock, java.io.Serializable {
 *
 * private static final long serialVersionUID = 7373984872572414699L;
 *
 * /** Synchronizer providing all implementation mechanics */

   // private final Sync sync;
//Sync 类的代码：
//    复制代码abstract static class Sync extends AbstractQueuedSynchronizer {...}
//    根据代码可知，Sync 有公平锁 FairSync 和非公平锁 NonfairSync两个子类：
//    复制代码static final class NonfairSync extends Sync {...}
//    static final class FairSync extends Sync {...}
//    下面我们来看一下公平锁与非公平锁的加锁方法的源码。
//    公平锁的锁获取源码如下：
//    复制代码protected final boolean tryAcquire(int acquires) {
//        final Thread current = Thread.currentThread();
//        int c = getState();
//        if (c == 0) {
//            if (!hasQueuedPredecessors() && //这里判断了 hasQueuedPredecessors()
//                    compareAndSetState(0, acquires)) {
//                setExclusiveOwnerThread(current);
//                return true;
//            }
//        } else if (current == getExclusiveOwnerThread()) {
//            int nextc = c + acquires;
//            if (nextc < 0) {
//                throw new Error("Maximum lock count exceeded");
//            }
//            setState(nextc);
//            return true;
//        }
//        return false;
//    }
    /**
     * 非公平锁的锁获取源码如下：
     * 复制代码final boolean nonfairTryAcquire(int acquires) {
     *     final Thread current = Thread.currentThread();
     *     int c = getState();
     *     if (c == 0) {
     *         if (compareAndSetState(0, acquires)) { //这里没有判断      hasQueuedPredecessors()
     *             setExclusiveOwnerThread(current);
     *             return true;
     *         }
     *     }
     *     else if (current == getExclusiveOwnerThread()) {
     *         int nextc = c + acquires;
     *         if (nextc < 0) // overflow
     *         throw new Error("Maximum lock count exceeded");
     *         setState(nextc);
     *         return true;
     *     }
     *     return false;
     * }
     *
     * 通过对比，我们可以明显的看出公平锁与非公平锁的 lock() 方法唯一的区别就在于公平锁在获取锁时多了一个限制条件：hasQueuedPredecessors() 为 false，这个方法就是判断在等待队列中是否已经有线程在排队了。这也就是公平锁和非公平锁的核心区别，如果是公平锁，那么一旦已经有线程在排队了，当前线程就不再尝试获取锁；对于非公平锁而言，无论是否已经有线程在排队，都会尝试获取一下锁，获取不到的话，再去排队。
     * 这里有一个特例需要我们注意，针对 tryLock() 方法，它不遵守设定的公平原则。
     * 例如，当有线程执行 tryLock() 方法的时候，一旦有线程释放了锁，那么这个正在 tryLock 的线程就能获取到锁，即使设置的是公平锁模式，即使在它之前已经有其他正在等待队列中等待的线程，简单地说就是 tryLock 可以插队。
     */
    /*看它的源码就会发现：
复制代码public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

这里调用的就是 nonfairTryAcquire()，表明了是不公平的，和锁本身是否是公平锁无关。
综上所述，公平锁就是会按照多个线程申请锁的顺序来获取锁，从而实现公平的特性。非公平锁加锁时不考虑排队等待情况，直接尝试获取锁，所以存在后申请却先获得锁的情况，但由此也提高了整体的效率。*/
    /**
     * 如果是公平锁，我们就在构造函数的参数中传入 true，如果是非公平锁，就在构造函数的参数中传入 false，
     * 默认是非公平锁。在获取读锁之前，线程会检查 readerShouldBlock() 方法，同样，在获取写锁之前，
     * 线程会检查 writerShouldBlock() 方法，来决定是否需要插队或者是去排队。
     * 首先看公平锁对于这两个方法的实现：
     *
     *
     *
     * 复制代码1.final boolean writerShouldBlock() {
     *
     *
     * 2.    return hasQueuedPredecessors();
     *
     *
     * 3.}
     *
     *
     * 4.final boolean readerShouldBlock() {
     *
     *
     * 5.    return hasQueuedPredecessors();
     *
     *
     * 6.}
     * 很明显，在公平锁的情况下，只要等待队列中有线程在等待，也就是 hasQueuedPredecessors() 返回 true 的时候，那么 writer 和 reader 都会 block，也就是一律不允许插队，都乖乖去排队，这也符合公平锁的思想。
     *
     * 下面让我们来看一下非公平锁的实现：
     *
     *
     *
     * 复制代码1.final boolean writerShouldBlock() {
     *
     *
     * 2.    return false; // writers can always barge
     *
     *
     * 3.}
     *
     *
     * 4.final boolean readerShouldBlock() {
     *
     *
     * 5.    return apparentlyFirstQueuedIsExclusive();
     *
     *
     * 6.}
     *
     */
}
